#include <tekari/metadata.h>

#include <sstream>
#include <vector>
#include <utility>

TEKARI_NAMESPACE_BEGIN

Metadata::Metadata()
:   m_is_spectral(false)
,   m_incident_angle(0.0f)
,   m_sample_name("Untilted")
,   m_points_in_file(-1)
,   m_data_points_per_loop(-1)
{}

void Metadata::add_line(const string& line)
{
    m_raw_metadata.push_back(line);
}

void Metadata::init_infos(VectorXf& wavelengths)
{
    const string* line;

    m_is_spectral = find_line_starting_with("#spectral data generated by") != nullptr;

    vector<pair<string, function<void(const string&, const string*)>>> parsing_funcs =
    {
        make_pair("#number of datapoints per loop in file:", [this](const string& keyword, const string* line) {
            sscanf(line->c_str() + keyword.length(), "%d",& m_data_points_per_loop);
        }),
        make_pair("#inphi", [this](const string& keyword, const string* line) { sscanf(line->c_str() + keyword.length(), "%f", &m_incident_angle[1]); }),
        make_pair("#phi_in", [this](const string& keyword, const string* line) { sscanf(line->c_str() + keyword.length(), "%f", &m_incident_angle[1]); }),
        make_pair("#intheta", [this](const string& keyword, const string* line) { sscanf(line->c_str() + keyword.length(), "%f", &m_incident_angle[0]); }),
        make_pair("#theta_in", [this](const string& keyword, const string* line) { sscanf(line->c_str() + keyword.length(), "%f", &m_incident_angle[0]); }),
        make_pair("#datapoints_in_file", [this](const string& keyword, const string* line) { sscanf(line->c_str() + keyword.length(), "%d", &m_points_in_file); }),
        make_pair("#sample_name", [this](const string& , const string* line) { m_sample_name = strip_quote_marks(line->substr(13)); }),
        make_pair("sample_name=", [this](const string& keyword, const string* line) {
            size_t pos = line->find(keyword);    // we know this will work
            m_sample_name = strip_quote_marks(line->substr(pos + keyword.length(), line->length()));
        }),
    };

    for (const auto& p : parsing_funcs)
    {
        if ((line = find_line_starting_with(p.first))   != nullptr ||
            (line = find_line_containing(p.first))      != nullptr)
            p.second(p.first, line);
    }

    if (m_is_spectral) {
        if (m_data_points_per_loop == -1)
            throw std::runtime_error("Invalid spectral data format (points per loop not found)");

        wavelengths.resize(m_data_points_per_loop);
        string* lambdas = find_line_starting_with("#lambda=");
        if (lambdas == nullptr)
            throw std::runtime_error("Invalid spectral data format (lambdas not found)");

        std::istringstream lambas_ss(*lambdas);
        lambas_ss.ignore(std::numeric_limits<std::streamsize>::max(), '=');
        float lambda;
        for (int i = 0; i < m_data_points_per_loop; ++i)
        {
            lambas_ss >> lambda;
            wavelengths[i] = lambda;
        }
    } else {
        if (m_data_points_per_loop != -1)
            throw std::runtime_error("Invalid standard data format (points per loop found)");
        if (m_points_in_file == -1)
            throw std::runtime_error("Invalid standard data format (points in file not found)");
        m_data_points_per_loop = 1;
    }
}

string* Metadata::find_line_containing(const string& target)
{
    for (auto& line : m_raw_metadata)
        if (line.find(target) != string::npos)
            return& line;
    return nullptr;
}

string* Metadata::find_line_starting_with(const string& target)
{
    for (auto& line : m_raw_metadata)
        if (line.find(target) == 0)
            return& line;
    return nullptr;
}

void Metadata::set_points_in_file(int points_in_file)
{
    m_points_in_file = points_in_file;

    const string dp_in_file_starter = "#datapoints_in_file\t";

    string* points_in_file_line = find_line_starting_with(dp_in_file_starter);
    if (points_in_file_line == nullptr)
        return;

   * points_in_file_line = dp_in_file_starter + to_string(points_in_file);
}

TEKARI_NAMESPACE_END